"""
Simplified DES Workflow Operations
"""

##############################
# Simple Operation Functions #
##############################
def xor(message, key):
    """
    Exclusive-OR
    
    Perform an exclusive OR of the message and the key.
    """
    result = ""
    for i in xrange(len(message)):
        if message[i] != key[i]:
            result += "1"
        else:
            result += "0"
    return result

def swap(left, right):
    """
    Swap Left and Right
    
    Swap the left and right halves of the message.
    """
    return right, left

def split_10(message):
    """
    10-bit Split
    
    Split a 10-bit message into two 5-bit messages.
    """
    return message[:5], message[5:]

def split_8(message):
    """
    8-bit Split
    
    Split an 8-bit message into two 4-bit messages.
    """
    return message[:4], message[4:]
    
def join(left, right):
    """
    Join
    
    Join two messages into one message.
    """
    return left + right

################################
# Rotation Operation Functions #
################################
def ls_one(key):
    """
    One-Bit Left Rotate
    
    Rotate the first five bits left one position, rotate the second 
    five bits left one position.
    """
    return key[1:5] + key[0] + key[6:10] + key[5]

def ls_two(key):
    """
    Two-Bit Left Rotate
    
    Rotate the first five bits left two positions, rotate the second five bits 
    left two positions.
    """
    return key[2:5] + key[0:2] + key[7:10] + key[5:7]

###################################
# Permutation Operation Functions #
###################################
def ip(message):
    """
    Initial Permutation
    
    Initial message permutation.
    """
    return _permute(message, [1, 5, 2, 0, 3, 7, 4, 6])

def ip_inv(message):
    """
    Inverse Initial Permutation
    
    Inverse of the message key permutation.
    """
    return _permute(message, [3, 0, 2, 4, 6, 1, 7, 5])

def p4(message):
    """
    P4 Permutation
    
    Permutes a 4-bit chunk of the message.
    """
    return _permute(message, [1, 3, 2, 0])

def p8(key):
    """
    P8 Permutation
    
    Picks out and permutes 8 of the 10 bits in the key.
    """
    return _permute(key, [5, 2, 6, 3, 7, 4, 9, 8])

def p10(key):
    """
    P10 Permutation
    
    Permutation of the 10-bit key.
    """
    return _permute(key, [2, 4, 1, 6, 3, 9, 0, 8, 7, 5])

def expansion_permutation(message):
    """
    Expansion and Permutation
    
    Expansion and permutation of a message.
    """
    return _permute(message, [3,0,1,2,1,2,3,0])

#############################
# S-Box Operation Functions #
#############################
def s0(message):
    """
    S0 S-Box
    
    S-Box mapping S0.
    """
    sbox = [
            ["01", "00", "11", "10"],
            ["11", "10", "01", "00"],
            ["00", "10", "01", "11"],
            ["11", "01", "11", "10"]
            ]
    row = int(message[0] + message[3], 2)
    column = int(message[1] + message[2], 2)
    return sbox[row][column]

def s1(message):
    """
    S1 S-Box
    
    S-Box mapping S1.
    """
    sbox = [
            ["00", "01", "10", "11"],
            ["10", "00", "01", "11"],
            ["11", "00", "01", "00"],
            ["10", "01", "00", "11"]
            ]
    row = int(message[0] + message[3], 2)
    column = int(message[1] + message[2], 2)
    return sbox[row][column]
    
##########################################
# Helper Functions for Internal Use Only #
##########################################
def _permute(key, permutation):
    """
    Apply permutation to key.
    """
    return "".join([key[i] for i in permutation])
    